﻿//TODO: Test?! Implement other modes? Noise

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

namespace LowLevelGraphics.Filter
{
    //TODO: Noise, add noise modes
    /// <summary>
    /// 
    /// </summary>
    public enum NOISE_MODE
    {
        /// <summary>
        /// 
        /// </summary>
        NONE,
        /// <summary>
        /// 
        /// </summary>
        RANDOMIZE_DELTA,
        /// <summary>
        /// 
        /// </summary>
        COLOR,
        /// <summary>
        /// 
        /// </summary>
        SALT_N_PEPPER,
        /// <summary>
        /// 
        /// </summary>
        SNOW,
        /// <summary>
        /// 
        /// </summary>
        WHITE_GAUSSIAN,

        /// <summary>
        /// 
        /// </summary>
        GAUSSIAN_BOX_MULLER_TRANSFORM,
        /// <summary>
        /// 
        /// </summary>
        RANDOMBITFLIP,
        /// <summary>
        /// 
        /// </summary>
        CHANNEL_FLIP,
        /// <summary>
        /// 
        /// </summary>
        CHANNEL_CROSSTALK,
        /// <summary>
        /// 
        /// </summary>
        CUSTOM,
        /// <summary>
        /// 
        /// </summary>
        RANGE, 
        /// <summary>
        /// 
        /// </summary>
        POISSON,
        /// <summary>
        /// 
        /// </summary>
        SPECKLE,
        /// <summary>
        /// 
        /// </summary>
        ISO
    }

    public delegate object NoiseDelegate();

    /// <summary>
    /// this is a filter that creates noise. 
    /// Choose between one of the noise modes.
    /// </summary>
    public class Noise : BaseImageFilter
    {
        protected NOISE_MODE M_NOISE_MODE = NOISE_MODE.COLOR;
        protected sbyte m_bMinValue = -3;
        protected sbyte m_bMaxValue = 3;
        protected int m_nProbability = 25;

        protected NoiseDelegate m_NoiseDelegate = new NoiseDelegate(delegate() { return new Random(DateTime.Now.Millisecond).Next(0,255); });

        /// <summary>
        /// default constructor
        /// </summary>
        public Noise()
        {
        }

        /// <summary>
        /// primary constructor specifying noise mode directly
        /// </summary>
        /// <param name="_NOISE_MODE"></param>
        public Noise(NOISE_MODE _NOISE_MODE)
        {
            M_NOISE_MODE = _NOISE_MODE;
        }

        /// <summary>
        /// used by clone to create a deep copy
        /// </summary>
        /// <param name="_noise"></param>
        internal Noise(Noise _noise)
        {
            this.M_NOISE_MODE = _noise.M_NOISE_MODE;
        }

        /// <summary>
        /// Gets or sets the propability.
        /// </summary>
        /// <value>The propability.</value>
        public int Propability
        {
            get { return m_nProbability; }
            set { m_nProbability = value; }
        }

        /// <summary>
        /// Gets or sets the noise mode.
        /// </summary>
        /// <value>The noise mode.</value>
        public NOISE_MODE NoiseMode
        {
            get { return M_NOISE_MODE; }
            set { M_NOISE_MODE = value; }
        }

        //TODO: check if probability is correct
        /// <summary>
        /// executes this filter
        /// </summary>
        /// <param name="_bitmap"></param>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            int x = 0;
            int y = 0;
            int nSeed = DateTime.Now.Millisecond * DateTime.Now.Millisecond;
            Random random = new Random(nSeed);
            Random randomProbability = new Random(random.GetHashCode());

            int nHeight = _bitmap.Height;
            int nWidth = _bitmap.Width;

            Color color = Color.Transparent;

            int nProbability = m_nProbability;
            int nPropabilityInternal = 100 / m_nProbability;
            int nMatchValue = nPropabilityInternal / 2;

            switch (M_NOISE_MODE)
            {
                case NOISE_MODE.RANDOMIZE_DELTA:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            nProbability = randomProbability.Next(0, nPropabilityInternal);
                            if (nProbability == 0)
                            {
                                color = _bitmap.GetPixel(x, y);
                                color = ColorFunctions.RandomizeDelta(color, random, m_bMinValue, m_bMaxValue);
                                _bitmap.SetPixel(x, y, color);
                            }
                        }
                    }
                    break;

                case NOISE_MODE.COLOR:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            nProbability = randomProbability.Next(0, nMatchValue);
                            if (nProbability == 0)
                            {
                                color = _bitmap.GetPixel(x, y);
                                color = ColorFunctions.Randomize(random, m_bMinValue, m_bMaxValue);
                                _bitmap.SetPixel(x, y, color);
                            }
                        }
                    }
                    break;

                case NOISE_MODE.RANDOMBITFLIP:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            nProbability = randomProbability.Next(0, nMatchValue);
                            if (nProbability == 0)
                            {
                                color = _bitmap.GetPixel(x, y);

                                int nColor = ColorFunctions.ToIntRGB(color);
                                //create random value
                                int nValue = random.Next(m_bMinValue, m_bMaxValue);
                                //get square of random value
                                nValue *= nValue;

                                nColor ^= nValue; //flip some bits randomly.
                                _bitmap.SetPixel(x, y, Color.FromArgb(nColor));
                            }
                        }
                    }
                    break;

                case NOISE_MODE.CHANNEL_CROSSTALK:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            nProbability = randomProbability.Next(0, nPropabilityInternal);
                            int nChannel = new Random().Next(0, 2);
                            if (nProbability == 0)
                            {
                                _bitmap.SetPixel(x, y, Definitions.White);
                            }
                        }
                    }
                    break;

                case NOISE_MODE.CHANNEL_FLIP:
                    Color colorFromArgb = Color.Transparent;
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            nProbability = randomProbability.Next(0, nPropabilityInternal);
                            if (nProbability == 0)
                            {
                                int nChannel1 = new Random().Next(0, 2);
                                int nChannel2 = new Random().Next(0, 2);
                                int nChannel3 = new Random().Next(0, 2);
                                colorFromArgb = _bitmap.GetPixel(x, y);
                                int nR = ColorFunctions.GetColorChannel(colorFromArgb, (RGB)nChannel1);
                                int nG = ColorFunctions.GetColorChannel(colorFromArgb, (RGB)nChannel2);
                                int nB = ColorFunctions.GetColorChannel(colorFromArgb, (RGB)nChannel3);
                                _bitmap.SetPixel(x, y, Color.FromArgb(nR, nG, nB));
                            }
                        }
                    }
                    break;


                case NOISE_MODE.SALT_N_PEPPER:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            bool bOnOff = new Random(DateTime.Now.Millisecond).Next(2) > 0 ? true : false;
                            nProbability = randomProbability.Next(0, nPropabilityInternal);
                            if (nProbability == 0)
                            {
                                _bitmap.SetPixel(x, y, bOnOff ? Definitions.White : Definitions.Black);
                            }
                        }
                    }
                    break;

                case NOISE_MODE.SNOW:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            nProbability = randomProbability.Next(0, nPropabilityInternal);
                            if (nProbability == 0)
                            {
                                color = _bitmap.GetPixel(x, y);
                                int nValue = random.Next(m_bMinValue, m_bMaxValue);
                                _bitmap.SetPixel(x, y, Definitions.White);
                            }
                        }
                    }
                    break;

                case NOISE_MODE.WHITE_GAUSSIAN:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            if (nProbability == 0)
                            {
                                color = _bitmap.GetPixel(x, y);
                                int nValue = random.Next(m_bMinValue, m_bMaxValue);
                                _bitmap.SetPixel(x, y, Definitions.White);
                            }
                        }
                    }
                    break; ;

                case NOISE_MODE.GAUSSIAN_BOX_MULLER_TRANSFORM:
                    int nCount = nWidth * nHeight / 100 * nProbability;
                    double dMax = Math.Sqrt(nWidth*nHeight);
                    double g1 = 0;
                    double g2 = 0;
                    for(int i=0;i<nCount;i++)
                    {
                        ImageMath.GaussianRand(ref g1, ref g2, dMax);
                        _bitmap.SetPixel((int)g1, (int)g2, Definitions.White);
                    }
                    break;

                case NOISE_MODE.RANGE:

                    int nTemp = 0;

                    int nMinX = new Random(DateTime.Now.Millisecond).Next(0, nWidth);
                    int nMaxX = new Random(nMinX).Next(0, nWidth);
                    int nMinY = new Random(nMaxX).Next(0, nHeight);
                    int nMaxY = new Random(nMinY).Next(0, nHeight);

                    int nXStep = new Random(nMaxY).Next(0, 10);
                    int nYStep = new Random(nXStep).Next(0, 10);

                    if (nMinX > nMaxX)
                    {
                        nTemp = nMinX;
                        nMinX = nMaxX;
                        nMaxX = nTemp;
                    }

                    if (nMinY > nMaxY)
                    {
                        nTemp = nMinY;
                        nMinY = nMaxY;
                        nMaxY = nTemp;
                    }

                    for (y = nMinY; y < nMaxY; y += nYStep)
                    {
                        for (x = nMinX; x < nMaxX; x += nXStep)
                        {
                            nProbability = randomProbability.Next(0, nPropabilityInternal);
                            if (nProbability == 0)
                            {
                                color = _bitmap.GetPixel(x, y);
                                int nValue = (int)m_NoiseDelegate();
                                _bitmap.SetGrayPixel(x, y, ImageMath.Clamp(nValue, 0, 255));
                            }
                        }
                    }
                    break;

                case NOISE_MODE.CUSTOM:
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            nProbability = randomProbability.Next(0, nPropabilityInternal);
                            if (nProbability == 0)
                            {
                                color = _bitmap.GetPixel(x, y);
                                int nValue = (int)m_NoiseDelegate();
                                _bitmap.SetPixel(x, y, Definitions.White);
                            }
                        }
                    }
                    break;

                case NOISE_MODE.POISSON:
                    break;

                case NOISE_MODE.SPECKLE:
                    break;

                case NOISE_MODE.ISO:
                    Color[] aColor = new Color[] { Color.Red, Color.Green, Color.Blue };
                    for (y = 0; y < nHeight; y++)
                    {
                        for (x = 0; x < nWidth; x++)
                        {
                            nProbability = randomProbability.Next(0, nPropabilityInternal);
                            if (nProbability == 0)
                            {
                                color = _bitmap.GetPixel(x, y);

                                _bitmap.SetPixel(x, y, aColor[new Random(x * y).Next(0, 3)]);
                            }
                        }
                    }
                    break;

            }
            return _bitmap;
        }

        /// <summary>
        /// used to create a deep copy
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Noise(this);
        }
    }
}

